﻿using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
using Kyoh.Lib.WinForms;
using System.Text;

namespace Kyoh.App.Renamer
{
    public sealed partial class RenameOptionControl : UserControl
    {
        private bool _loaded;

        private int _optionIndex;
        private RenameOptionCollection _options;

        private bool _applyChangedOption = true;
        private object _optionsComboBoxLocker = new object();
        private bool _systemSelectingOption;

        public RenameOptionControl()
        {
            InitializeComponent();
        }

        private static Properties.Settings Conf { get { return Properties.Settings.Default; } }

        protected override Size DefaultMinimumSize { get { return new Size(300, 430); } }

        protected override Size DefaultSize { get { return new Size(300, 430); } }

        internal void LoadConfig(RenamerArgument args)
        {
            // PathHistory
            Properties.Settings.LoadHistComboBox(histComboBoxPath, Properties.Settings.Default.PathHistory);

            try
            {
                if (args == null)
                    using (var reader = new StringReader(Conf.SerializedBatch))
                        _options = RenameOptionCollection.Deserialize(reader);
                else
                    LoadBatchFile(args.BatchFile);
            }
            catch { _options = null; }

            _loaded = true;
            if (_options == null)
            {
                ApplyFilter();
                ApplyOption();
            }
         
            ShowFilter();
            ShowOption();
        }

        public void SaveConfig()
        {
            ShowOption();

            // PathHistory
            Properties.Settings.SaveHistComboBox(histComboBoxPath, "PathHistory");

            if (_options == null)
            {
                Conf.SerializedBatch = string.Empty;
                Conf.BatchIndex = 0;
            }
            else
            {
                using (var writer = new StringWriter())
                {
                    RenameOptionCollection.Serialize(writer, _options);
                    Conf.SerializedBatch = writer.ToString();
                    Conf.BatchIndex = _optionIndex;
                }
            }
        }

        public event EventHandler OptionChanged;

        private void OnOptionChanged(object sender, EventArgs e)
        {
            RaiseOptionChanged(sender, e, false);
        }

        private void RaiseOptionChanged(object sender, EventArgs e, bool fource)
        {
            var changed = fource;
            if (_applyChangedOption)
                changed |= ApplyFilter() | ApplyOption();

            var handler = OptionChanged;
            if (changed && handler != null)
                handler(sender, e);
        }

        public void SelectTarget()
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
                histComboBoxPath.Text = folderBrowserDialog.SelectedPath;
            if (System.IO.Path.IsPathRooted(histComboBoxPath.Text))
                folderBrowserDialog.SelectedPath = histComboBoxPath.Text;
        }

        private void buttonFolderDialog_Click(object sender, EventArgs e)
        {
            SelectTarget();
        }

        internal RenameOptionCollection GetOptions()
        {
            return _options;
        }

        public string Path
        {
            get { return _options == null ? string.Empty : _options.Path; }
            set { histComboBoxPath.Text = value; }
        }

        public void ShiftOption(int i, int p)
        {
            if (i < 0)
                return;
            ApplyOption();
            
            var option = _options[i];
            _options.RemoveAt(i);
            if (p < 0)
            {
                _options.Add(option);
                SelectQueue(false, _options.Count - 1);
            }
            else
            {
                _options.Insert(p, option);
                SelectQueue(false, p);
            }
            RaiseOptionChanged(this, EventArgs.Empty, true);
        }

        private bool ApplyFilter()
        {
            if (!_loaded)
                return false;
            if (_options == null)
            {
                _options = new RenameOptionCollection(
                    histComboBoxPath.Text,
                    textBoxFilter.Text,
                    checkBoxFilterIgnoreCase.Checked,
                    checkBoxDirectory.Checked,
                    checkBoxFile.Checked,
                    checkBoxSubdirectories.Checked
                        ? decimal.ToInt32(numericUpDownDepth.Value)
                        : 0);
                return true;
            }
            else
            {
                return _options.ApplyFilter(
                    histComboBoxPath.Text,
                    textBoxFilter.Text,
                    checkBoxFilterIgnoreCase.Checked,
                    checkBoxDirectory.Checked,
                    checkBoxFile.Checked,
                    checkBoxSubdirectories.Checked
                        ? decimal.ToInt32(numericUpDownDepth.Value)
                        : 0);
            }
        }

        private bool ApplyOption()
        {
            if (!_loaded)
                return false;
            var method = (PatternMethod)comboBoxMethod.SelectedValue;

            if (_options.Count == 0)
            {
                _options.Add(new RenameOption(checkBoxForEachDirectory.Checked,
                                     decimal.ToInt32(numericUpDownSerialStart.Value),
                                     decimal.ToInt32(numericUpDownSerialStep.Value),
                                     (NumberStyle)comboBoxNumberStyle.SelectedValue,
                                     method,
                                     textBoxSearch.Text,
                                     textBoxReplace.Text,
                                     checkBoxSearchIgnoreCase.Checked,
                                     checkBoxExcept.Checked));
                _optionIndex = 0;
                return true;
            }
            else
            {
                return _options.ApplyOption(_optionIndex,
                                    checkBoxForEachDirectory.Checked,
                                    decimal.ToInt32(numericUpDownSerialStart.Value),
                                    decimal.ToInt32(numericUpDownSerialStep.Value),
                                    (NumberStyle)comboBoxNumberStyle.SelectedValue,
                                    method,
                                    textBoxSearch.Text,
                                    textBoxReplace.Text,
                                    checkBoxSearchIgnoreCase.Checked,
                                     checkBoxExcept.Checked);
            }
        }

        private static RenameOption CreateRenameOption(PatternMethod method)
        {
            return new RenameOption(Conf.DefaultSerialForEachDirectory, decimal.ToInt32(Conf.DefaultSerialStart), decimal.ToInt32(Conf.DefaultSerialStep), Conf.DefaultSerialNumberStyle,
                Conf.DefaultRenameMethod, string.Empty, string.Empty, Conf.DefaultIgnoreCase, Conf.DefaultExceptFromList);
        }

        internal void AddNewOption(PatternMethod method)
        {
            if (!_loaded)
                return;
            ApplyOption();
            _options.Add(CreateRenameOption(method));
            _optionIndex = _options.Count - 1;
            ShowOption();
            OnOptionChanged(this, EventArgs.Empty);
        }

        public void RemoveOption()
        {
            if (!_loaded)
                return;

            if (MemorizedConfirmationDialog.Show(Properties.Settings.Default.PropertyValues["ConfirmRemovingAgain"], string.Format(Properties.TextResources.ConfirmationToRemoveQueue, _options[_optionIndex].ToString()), Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation, owner: this) != DialogResult.OK)
                return;

            _options.RemoveAt(_optionIndex);
            if (_options.Count == 0)
            {
                _options.Add(CreateRenameOption(Conf.DefaultRenameMethod));
                _optionIndex = 0;
                ShowOption();
            }
            else
            {
                if (_optionIndex >= _options.Count)
                    _optionIndex = _options.Count - 1;
                ShowOption();
            }
            RaiseOptionChanged(this, EventArgs.Empty, true);
        }

        public void ClearOptions()
        {
            if (MemorizedConfirmationDialog.Show(Properties.Settings.Default.PropertyValues["ConfirmClearingAgain"], Properties.TextResources.ConfirmationToClearBatch, Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation, owner: this) != DialogResult.OK)
                return;

            if (!_loaded)
                return;

            _options.Clear();
            _options.Add(CreateRenameOption(Conf.DefaultRenameMethod));
            _optionIndex = 0;

            ShowOption();
            RaiseOptionChanged(this, EventArgs.Empty, true);
        }

        /// <summary>変更されたフィルタの内容をコントロールに反映します。</summary>
        private void ShowFilter()
        {
            if (!_loaded)
                return;

            _applyChangedOption = false;

            histComboBoxPath.Text = _options.Path;
            textBoxFilter.Text = _options.Filter;
            checkBoxFilterIgnoreCase.Checked = _options.IgnoreCase;
            checkBoxDirectory.Checked = _options.ContainsDirectory;
            checkBoxFile.Checked = _options.ContainsFile;
            checkBoxSubdirectories.Checked = _options.Depth > 0;
            numericUpDownDepth.Value = _options.Depth;

            _applyChangedOption = true;
        }

        /// <summary>変更されたオプションの内容をコントロールに反映します。</summary>
        private void ShowOption()
        {
            if (!_loaded)
                return;

            _applyChangedOption = false;

            comboBoxMethod.SelectedValue = _options[_optionIndex].Method;
            textBoxSearch.Text = _options[_optionIndex].Pattern;
            textBoxReplace.Text = _options[_optionIndex].Replacement;
            checkBoxSearchIgnoreCase.Checked = _options[_optionIndex].IgnoreCase;

            if (comboBoxBatchQueueIndex.Items.Count > 0)
                comboBoxBatchQueueIndex.Items.Clear();
            for (int i = 0; i < _options.Count; i++)
            {
                comboBoxBatchQueueIndex.Items.Add(i + 1);
            }
            lock (_optionsComboBoxLocker)
            {
                _systemSelectingOption = true;
                comboBoxBatchQueueIndex.SelectedIndex = _optionIndex;
                _systemSelectingOption = false;
            }
            labelBatchQueueCount.Text = _options.Count.ToString();
            checkBoxForEachDirectory.Checked = _options[_optionIndex].Grouped;
            comboBoxNumberStyle.SelectedValue = _options[_optionIndex].Style;
            numericUpDownSerialStart.Value = _options[_optionIndex].Start;
            numericUpDownSerialStep.Value = _options[_optionIndex].Step;
            comboBoxNumberStyle.SelectedValue = _options[_optionIndex].Style;
            checkBoxExcept.Checked = _options[_optionIndex].Except;

            _applyChangedOption = true;
        }

        private void buttonAddBatchQueue_Click(object sender, EventArgs e)
        {
            AddNewOption(Conf.DefaultRenameMethod);
        }

        private void buttonRemoveBatchQueue_Click(object sender, EventArgs e)
        {
            RemoveOption();
        }

        public void SelectQueue(bool apply, int index)
        {
            if (0 <= index && index < _options.Count)
            {
                if (apply)
                    ApplyOption();
                _optionIndex = index;
                ShowOption();
            }
        }

        public int SelectedIndex { get { return _optionIndex; } }

        private void buttonNextBatchQueue_Click(object sender, EventArgs e)
        {
            SelectQueue(true, _optionIndex + 1);
        }

        private void buttonPrevBatchQueue_Click(object sender, EventArgs e)
        {
            SelectQueue(true, _optionIndex - 1);
        }

        private void comboBoxBatchQueueIndex_SelectedIndexChanged(object sender, EventArgs e)
        {
            lock (_optionsComboBoxLocker)
                if (_systemSelectingOption == false)
                    if (_options != null)
                        SelectQueue(true, comboBoxBatchQueueIndex.SelectedIndex);
        }

        public void OpenQueueManagerDialog()
        {
            ApplyFilter();
            ApplyOption();
            using (var form = new RenameQueueManager(_options))
            {
                form.ShowDialog(ParentForm);
                _options = form.Batch;
            }
            if (_options != null)
                _optionIndex = Math.Min(_optionIndex, _options.Count - 1);
            ShowFilter();
            ShowOption();
        }

        private void renameQueueManagerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenQueueManagerDialog();
        }

        public void SaveBatch()
        {
            ApplyFilter();
            ApplyOption();
            if (saveFileDialogBatchFile.ShowDialog() == DialogResult.OK)
                using (var stream = new FileStream(saveFileDialogBatchFile.FileName, FileMode.Create))
                    RenameOptionCollection.Serialize(stream, _options);
        }

        public void LoadBatch()
        {
            string fileName;

            if (openFileDialogBatchFile.ShowDialog() == DialogResult.OK)
                fileName = openFileDialogBatchFile.FileName;
            else
                return;

            LoadBatchFile(fileName);

            ShowFilter();
            ShowOption();

            RaiseOptionChanged(this, EventArgs.Empty, true);
        }

        private void saveABatchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveBatch();
        }

        private void loadABatchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LoadBatch();
        }

        private void LoadBatchFile(string fileName)
        {
            RenameOptionCollection batch;
            try
            {
                using (var reader = new FileStream(fileName, FileMode.Open))
                    batch = RenameOptionCollection.Deserialize(reader);
                _options = batch;
                _optionIndex = 0;
            }
            catch (Exception exc)
            {
                MessageBox.Show("Failed to load a batch file from '" + fileName + "'. (Error: " + exc.Message, "Error: Load a batch", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private static void ShowPullDownMenu(object sender, ToolStripDropDown menu)
        {
            var control = sender as Control;
            if (control != null && menu != null)
                menu.Show(control, 0, control.Height);
        }

        private void buttonBatchMenu_Click(object sender, EventArgs e)
        {
            ShowPullDownMenu(sender, contextMenuStripBatchMenu);
        }

        private void clearABatchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ClearOptions();
        }
    }
}